﻿using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;

namespace Jerry
{
    public class ABSubPackageTool : MonoBehaviour
    {
        /// <summary>
        /// 当前版本号，将写入版本控制文件
        /// </summary>
        [Tooltip("当前版本号")]
        public string Ver = "0.0.0";

        private GUILayoutOption[] btnOpt = new GUILayoutOption[2]
        {
            GUILayout.MinHeight(30),
            GUILayout.MinWidth(80),
        };

        /// <summary>
        /// 打出的Bundle的路径
        /// </summary>
        private string m_BundleAssetsPath;

        /// <summary>
        /// Manifest加载状态
        /// </summary>
        private ManifestLoadState m_ManifestLoadState = ManifestLoadState.NotStart;
        /// <summary>
        /// Manifest文件所在Bundle
        /// </summary>
        private AssetBundle m_ManifestBundle = null;
        /// <summary>
        /// Manifest文件
        /// </summary>
        private AssetBundleManifest m_Manifest = null;
        /// </summary>
        /// <summary>
        /// 颜色库
        /// </summary>
        private Color[] m_ColorsLib = new Color[5]
        {
            Color.yellow,
            Color.red,
            Color.cyan,
            Color.blue,
            Color.gray,
        };
        /// <summary>
        /// 当前分包索引
        /// </summary>
        private int m_CurPackageIdx = 0;
        /// <summary>
        /// 提示信息
        /// </summary>
        private string m_TipInfo = "一切正常";
        /// <summary>
        /// Bundle信息管理
        /// </summary>
        private BundleInfoMgr m_BundleInfoMgr = null;

        /// <summary>
        /// 排序大小优先，而不是名称
        /// </summary>
        private bool sortSizeFirst = true;

        void Awake()
        {
            m_BundleAssetsPath = Application.dataPath + "/../" + PlatformUtil.AssetBundlesOutputPath + "/" + PlatformUtil.CurPlatform.ToString() + "/";
            m_BundleInfoMgr = new BundleInfoMgr();
            m_BundleInfoMgr.SetPersistentDataPath(Application.dataPath + "/../");
        }

        void OnGUI()
        {
            GUILayout.BeginVertical();
            GUILayout.Label(m_TipInfo);
            OnGUIControl();
            GUILayout.Space(5);

            if (m_ManifestLoadState == ManifestLoadState.LoadFinish)
            {
                GUILayout.BeginHorizontal();
                float nameAmt = 0;//当前这行已经显示的名字长度
                float oneLineAmt = Screen.width * 1.0f / 7.5f;//一行显示的字符数量
                for (int i = 0; i < m_BundleInfoMgr.InfoList.Count; i++)
                {
                    if (nameAmt + m_BundleInfoMgr.InfoList[i].name.Length > oneLineAmt)
                    {
                        GUILayout.EndHorizontal();
                        GUILayout.BeginHorizontal();
                        nameAmt = 0;
                    }
                    GUI.color = m_ColorsLib[m_BundleInfoMgr.InfoList[i].packageLev];
                    if (GUILayout.Button(string.Format("{0}\n{1}MB", m_BundleInfoMgr.InfoList[i].name, MBSize(m_BundleInfoMgr.InfoList[i].size).ToString("F2")), btnOpt))
                    {
                        m_BundleInfoMgr.InfoList[i].packageLev = m_CurPackageIdx;
                        ChangeDependencyPackage(m_BundleInfoMgr.InfoList[i]);
                    }
                    GUI.color = Color.white;
                    nameAmt += m_BundleInfoMgr.InfoList[i].name.Length;
                }
                GUILayout.EndHorizontal();
            }
            GUILayout.EndVertical();
        }

        private void OnGUIControl()
        {
            GUILayout.BeginHorizontal();
            switch (m_ManifestLoadState)
            {
                case ManifestLoadState.NotStart:
                    {
                        GUI.color = Color.white;
                    }
                    break;
                case ManifestLoadState.Loading:
                    {
                        GUI.color = Color.grey;
                    }
                    break;
                case ManifestLoadState.LoadFinish:
                    {
                        GUI.color = Color.green;
                    }
                    break;
                case ManifestLoadState.LoadError:
                    {
                        GUI.color = Color.red;
                    }
                    break;
            }
            if (GUILayout.Button("加载配置", btnOpt))
            {
                if (m_ManifestLoadState != ManifestLoadState.Loading)
                {
                    m_ManifestLoadState = ManifestLoadState.Loading;
                    m_Manifest = null;
                    if (m_ManifestBundle != null)
                    {
                        m_ManifestBundle.Unload(true);
                        m_ManifestBundle = null;
                    }
                    this.StartCoroutine(IE_LoadManifest());
                }
            }
            GUI.color = Color.white;

            if (m_ManifestLoadState == ManifestLoadState.LoadFinish)
            {
                GUI.color = m_ColorsLib[m_CurPackageIdx];
                if (GUILayout.Button(string.Format("分包{0}\n{1}MB", m_CurPackageIdx + 1, GetSelectPackageSize().ToString("F2")), btnOpt))
                {
                    m_CurPackageIdx = (m_CurPackageIdx + 1) % m_ColorsLib.Length;
                }
                GUI.color = Color.white;

                if (GUILayout.Button(string.Format("整理排序({0})", sortSizeFirst ? "大小" : "名称"), btnOpt))
                {
                    sortSizeFirst = !sortSizeFirst;
                    DoSort();
                }

                if (GUILayout.Button("冲突检测", btnOpt))
                {
                    CheckError();
                }

                if (GUILayout.Button("输出信息", btnOpt))
                {
                    OutInfo();
                }
            }
            GUILayout.EndHorizontal();
        }

        /// <summary>
        /// 输出信息
        /// </summary>
        private void OutInfo()
        {
            sortSizeFirst = false;
            DoSort();

            string outJsonFilePath = Application.dataPath + "/../" + BundleInfoMgr.m_VersionFileName;
            File.WriteAllText(outJsonFilePath, JsonUtility.ToJson(m_BundleInfoMgr, true), Encoding.UTF8);

            m_TipInfo = "输出完成";
        }

        /// <summary>
        /// 冲突检测
        /// </summary>
        private void CheckError()
        {
            m_TipInfo = "冲突检测中...";
            for (int i = 0; i < m_BundleInfoMgr.InfoList.Count; i++)
            {
                if (m_BundleInfoMgr.InfoList[i].dependencies == null || m_BundleInfoMgr.InfoList[i].dependencies.Count <= 0)
                {
                    continue;
                }
                foreach (BundleInfo bi in m_BundleInfoMgr.InfoList)
                {
                    if (m_BundleInfoMgr.InfoList[i].dependencies.Contains(bi.name)
                        && bi.packageLev > m_BundleInfoMgr.InfoList[i].packageLev)
                    {
                        m_TipInfo = string.Format("冲突:{0}依赖{1}，但是不在同一个或更前的分包里", m_BundleInfoMgr.InfoList[i].name, bi.name);
                        return;
                    }
                }
            }
            m_TipInfo = "没有冲突";
        }

        /// <summary>
        /// 改变依赖的分包
        /// </summary>
        private void ChangeDependencyPackage(BundleInfo info)
        {
            if (info == null || info.dependencies == null || info.dependencies.Count <= 0)
            {
                return;
            }
            foreach (BundleInfo bi in m_BundleInfoMgr.InfoList)
            {
                if (info.dependencies.Contains(bi.name)
                    && bi.packageLev > info.packageLev)
                {
                    bi.packageLev = info.packageLev;
                }
            }
        }

        /// <summary>
        /// 排序
        /// </summary>
        private void DoSort()
        {
            m_BundleInfoMgr.InfoList.Sort((x, y) =>
            {
                if (x.packageLev != y.packageLev)
                {
                    return x.packageLev.CompareTo(y.packageLev);
                }
                if (sortSizeFirst)
                {
                    if (x.size != y.size)
                    {
                        return -x.size.CompareTo(y.size);
                    }
                    return x.name.CompareTo(y.name);
                }
                else
                {
                    if (!x.name.Equals(y.name))
                    {
                        return x.name.CompareTo(y.name);
                    }
                    return -x.size.CompareTo(y.size);
                }
            });
        }

        /// <summary>
        /// 获得选中包的总大小
        /// </summary>
        /// <returns></returns>
        private float GetSelectPackageSize()
        {
            long ret = 0;
            foreach (BundleInfo bi in m_BundleInfoMgr.InfoList)
            {
                if (bi.packageLev == m_CurPackageIdx)
                {
                    ret += bi.size;
                }
            }
            return MBSize(ret);
        }

        /// <summary>
        /// 加载Manifest
        /// </summary>
        /// <returns></returns>
        private IEnumerator IE_LoadManifest()
        {
            WWW www = new WWW("file://" + m_BundleAssetsPath + PlatformUtil.CurPlatform.ToString());
            yield return new WaitUntil(() => www.isDone || www.error != null);
            if (!string.IsNullOrEmpty(www.error))
            {
                m_TipInfo = www.error;
                m_ManifestLoadState = ManifestLoadState.LoadError;
                yield break;
            }
            m_ManifestBundle = www.assetBundle;
            AssetBundleRequest abr = m_ManifestBundle.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
            yield return new WaitUntil(() => abr.isDone);
            m_Manifest = abr.asset as AssetBundleManifest;
            if (m_Manifest == null)
            {
                m_TipInfo = "Manifest加载失败";
                m_ManifestLoadState = ManifestLoadState.LoadError;
                yield break;
            }

            BundleInfoMgr oldInfoMgr = null;
            string resJsonFilePath = Application.dataPath + "/../" + BundleInfoMgr.m_VersionFileName;
            if (File.Exists(resJsonFilePath))
            {
                oldInfoMgr = JsonUtility.FromJson<BundleInfoMgr>(File.ReadAllText(resJsonFilePath));
            }

            string[] bds = m_Manifest.GetAllAssetBundles();
            foreach (string bd in bds)
            {
                if (!m_BundleInfoMgr.InfoList.Exists((x) => x.name.Equals(bd)))
                {
                    int pLev = m_CurPackageIdx;
                    if (oldInfoMgr != null)
                    {
                        BundleInfo info = oldInfoMgr.InfoList.Find((x) => x.name.Equals(bd));
                        if (info != null)
                        {
                            pLev = info.packageLev;
                        }
                    }
                    BundleInfo newInfo = new BundleInfo()
                    {
                        name = bd,
                        size = GetFileSize(bd),
                        hash = m_Manifest.GetAssetBundleHash(bd).ToString(),
                        packageLev = pLev,
                        dependencies = new List<string>(m_Manifest.GetAllDependencies(bd))
                    };
                    newInfo.dependencies.Sort((x, y) =>
                    {
                        return x.CompareTo(y);
                    });
                    m_BundleInfoMgr.InfoList.Add(newInfo);
                }
            }
            DoSort();
            m_TipInfo = "Manifest加载完成";
            m_ManifestLoadState = ManifestLoadState.LoadFinish;
        }

        /// <summary>
        /// 获取文件大小
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private long GetFileSize(string fileName)
        {
            string filePath = m_BundleAssetsPath + fileName;
            if (File.Exists(filePath))
            {
                FileInfo info = new FileInfo(filePath);
                if (info != null)
                {
                    return info.Length;
                }
            }
            return 0;
        }

        /// <summary>
        /// Manifest加载状态
        /// </summary>
        public enum ManifestLoadState
        {
            /// <summary>
            /// 未开始
            /// </summary>
            NotStart = 0,
            /// <summary>
            /// 加载中
            /// </summary>
            Loading,
            /// <summary>
            /// 加载完成
            /// </summary>
            LoadFinish,
            /// <summary>
            /// 加载失败
            /// </summary>
            LoadError,
        }

        /// <summary>
        /// 文件大小，单位MB
        /// </summary>
        public float MBSize(long fileSize)
        {
            return fileSize * 1.0f / 1024 / 1024;
        }

        #region 测试依赖

        /// <summary>
        /// 测试的Bundle
        /// </summary>
        [Tooltip("需要测试依赖的bundle")]
        public string TestBundleName = "";

        /// <summary>
        /// 测试依赖
        /// </summary>
        [ContextMenu("TestDependencies")]
        private void TestDependencies()
        {
            if (!Application.isPlaying)
            {
                UnityEngine.Debug.LogWarning("运行时才可用");
                return;
            }
            if (!m_Manifest)
            {
                UnityEngine.Debug.LogWarning("Manifest文件加载后才可用");
                return;
            }
            if (string.IsNullOrEmpty(TestBundleName))
            {
                UnityEngine.Debug.LogWarning("请输入要测试的BundleName");
                return;
            }
            if (new List<string>(m_Manifest.GetAllAssetBundles()).Contains(TestBundleName) == false)
            {
                UnityEngine.Debug.LogWarning("[" + TestBundleName + "]不存在");
                return;
            }
            UnityEngine.Debug.LogWarning("test:" + TestBundleName);
            bool find = false;
            foreach (string s in m_Manifest.GetAllDependencies(TestBundleName))
            {
                find = true;
                UnityEngine.Debug.LogWarning(s);
            }
            if (!find)
            {
                UnityEngine.Debug.LogWarning("无依赖");
            }
        }

        [ContextMenu("Test")]
        private void Test()
        {
            UnityEngine.Debug.LogWarning(JsonUtility.ToJson(m_BundleInfoMgr, true));
        }

        #endregion 测试依赖
    }
}